/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.scs.ubbcluj.backend;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import static edu.scs.ubbcluj.backend.MongoDB.construct;
import static edu.scs.ubbcluj.backend.MongoDB.db;
import static edu.scs.ubbcluj.backend.MongoDB.getRecordById;
import edu.scs.ubbcluj.model.Attribute;
import edu.scs.ubbcluj.model.Condition;
import edu.scs.ubbcluj.model.Table;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;

/**
 *
 * @author Torok
 */
public class Selection {

    public static List<List<String>> getMatchingRows(Table table, List<Condition> conditions, List<Attribute> wantedAttrs) {
        List<Condition> simpleCond = new ArrayList();
        List<Condition> indCond = new ArrayList();
        for (Condition cond : conditions) {
            if (table.getindexedAttributes().contains(cond.getAttr())) {
                indCond.add(cond);
            } else {
                simpleCond.add(cond);
            }
        }
        if (!indCond.isEmpty()) {
            return getFilteredElementsWithIndex(table, indCond, simpleCond, wantedAttrs);
        } else {
            return getFilteredElements(table, simpleCond, wantedAttrs);
        }
    }

    private static List<List<String>> getFilteredElementsWithIndex(Table table, List<Condition> indexCond, List<Condition> simpleCond, List<Attribute> wantedAttrs) {

        Set<String> keys = new HashSet();
        for (Condition cond : indexCond) {
            DBCollection coll = MongoDB.db.getCollection(table.getName() + cond.getAttr().getName() + ".ind");
            DBCursor cursor = coll.find();
            List<String> values1 = new ArrayList();
            try {
                while (cursor.hasNext()) {
                    DBObject o = cursor.next();
                    boolean add = true;
                    String key = (String) o.get("key");
                    if (key != null) {
                        add = checkMatch(key, cond.getOperation(), cond.getCorValue());
                    }
                    String value = (String) o.get("value");
                    if (add && value != null) {
                        values1.add(value);
                    }
                }
            } finally {
                cursor.close();
            }
            for (String row : values1) {
                String[] helper = row.split(",");
                for (String s : helper) {
                    keys.add(s);
                }
            }
        }
        List<List<String>> values = new ArrayList();
        for (String s : keys) {
            List<String> theOne = getFilteredRecordById(table, s, simpleCond, wantedAttrs);
            if (theOne != null) {
                values.add(theOne);
            }
        }
        return values;
    }

    public static List<String> getFilteredRecordById(Table table, String id, List<Condition> simpleCond, List<Attribute> wantedAttrs) {
        DBCollection collection = db.getCollection(table.getName());
        BasicDBObject query = new BasicDBObject();
        //tobbszoros primary key eseten igy nem jo:
        query.append("key", construct(table.getPrimaryKeys().get(0), id));
        DBCursor cursor = collection.find(query);
        List<String> row = new ArrayList();
        try {
            if (cursor.hasNext()) {
                DBObject o = cursor.next();
                String key = (String) o.get("key");
                if (key != null) {
                    for (Attribute attr : table.getPrimaryKeys()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(key, 0, nr);
                        //System.out.println("key: *" + v + "*");
                        if (wantedAttrs.contains(attr)) {
                            row.add(v);
                        }
                        key = key.substring(nr);
                    }
                }
                boolean add;
                String value = (String) o.get("value");
                if (value != null) {
                    for (Attribute attr : table.getAttributes()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(value, 0, nr);
                        //System.out.println("value: *" + v + "*");
                        add = true;
                        for (Condition cond : simpleCond) {
                            if (attr.getName().equals(cond.getAttr().getName())) {
                                add = checkMatch(v, cond.getOperation(), cond.getCorValue());
                            }
                        }
                        if (!add) {
                            return null;
                        }
                        if (wantedAttrs.contains(attr)) {
                            row.add(v);
                        }
                        value = value.substring(nr);
                    }
                    for (Attribute attr : table.getForeignKeys()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(value, 0, nr);
                        //System.out.println("value: *" + v + "*");
                        add = true;
                        for (Condition cond : simpleCond) {
                            if (attr.getName().equals(cond.getAttr().getName())) {
                                add = checkMatch(v, cond.getOperation(), cond.getCorValue());
                            }
                        }
                        if (!add) {
                            return null;
                        }
                        if (wantedAttrs.contains(attr)) {
                            row.add(v);
                        }
                        value = value.substring(nr);
                    }
                }
            }
        } finally {
            cursor.close();
        }
        return row;
    }

    private static List<List<String>> getFilteredElements(Table table, List<Condition> conditions, List<Attribute> wantedAttrs) {
        DBCollection coll = MongoDB.db.getCollection(table.getName());
        DBCursor cursor = coll.find();
        List<List<String>> values = new ArrayList();
        try {
            while (cursor.hasNext()) {
                List<String> row = new ArrayList();
                DBObject o = cursor.next();
                boolean add = true;
                String key = (String) o.get("key");
                if (key != null) {
                    for (Attribute attr : table.getPrimaryKeys()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(key, 0, nr);
                        if (wantedAttrs.contains(attr)) {
                            row.add(v);
                        }
                        key = key.substring(nr);
                        for (Condition cond : conditions) {
                            if (attr.getName().equals(cond.getAttr().getName())) {
                                add = checkMatch(v, cond.getOperation(), cond.getCorValue());
                            }
                        }
                    }
                }
                String value = (String) o.get("value");
                if (value != null) {
                    for (Attribute attr : table.getAttributes()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(value, 0, nr);
                        if (wantedAttrs.contains(attr)) {
                            row.add(v);
                        }
                        value = value.substring(nr);
                        if (add) {
                            for (Condition cond : conditions) {
                                if (attr.getName().equals(cond.getAttr().getName())) {
                                    add = checkMatch(v, cond.getOperation(), cond.getCorValue());
                                }
                            }
                        }
                    }
                    for (Attribute attr : table.getForeignKeys()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(value, 0, nr);
                        if (wantedAttrs.contains(attr)) {
                            row.add(v);
                        }
                        value = value.substring(nr);
                        if (add) {
                            for (Condition cond : conditions) {
                                if (attr.getName().equals(cond.getAttr().getName())) {
                                    add = checkMatch(v, cond.getOperation(), cond.getCorValue());
                                }
                            }
                        }
                    }
                }
                if (add) {
                    values.add(row);
                }
            }
        } finally {
            cursor.close();
        }
        return values;
    }

    public static boolean checkMatch(String value, String operation, String corValue) {
        String v = value.trim();
        if (operation.equals("equal")) {
            if (!v.equals(corValue)) {
                return false;
            }
        } else if (operation.equals("not equal")) {
            if (v.equals(corValue)) {
                return false;
            }
        } else if (operation.equals("=")) {
            int v1 = Integer.parseInt(v);
            int v2 = Integer.parseInt(corValue);
            if (v1 != v2) {
                return false;
            }
        } else if (operation.equals("!=")) {
            int v1 = Integer.parseInt(v);
            int v2 = Integer.parseInt(corValue);
            if (v1 == v2) {
                return false;
            }
        } else if (operation.equals("<")) {
            int v1 = Integer.parseInt(v);
            int v2 = Integer.parseInt(corValue);
            if (v1 >= v2) {
                return false;
            }
        } else if (operation.equals("<=")) {
            int v1 = Integer.parseInt(v);
            int v2 = Integer.parseInt(corValue);
            if (v1 > v2) {
                return false;
            }
        } else if (operation.equals(">")) {
            int v1 = Integer.parseInt(v);
            int v2 = Integer.parseInt(corValue);
            if (v1 <= v2) {
                return false;
            }
        } else if (operation.equals(">=")) {
            int v1 = Integer.parseInt(v);
            int v2 = Integer.parseInt(corValue);
            if (v1 < v2) {
                return false;
            }
        }
        return true;
    }

    public static List<List<String>> oderBy(Table tempTable, Attribute theOne, String orderType) {
        //oderType: ASC or DESC
        DBCollection coll = MongoDB.db.getCollection(tempTable.getName());
        DBCursor cursor = coll.find();
        List<List<String>> values = new ArrayList();
        List<List<String>> sortedValues = new ArrayList();
        int i = 0;
        int pack = 0;
        int maxnr = 5;
        Table table1 = new Table(tempTable.getName() + "partsort");
        table1.setPrimaryKeys(tempTable.getPrimaryKeys());
        table1.setAttributes(tempTable.getAttributes());
        table1.setForeignKeys(tempTable.getForeignKeys());
        int place = 0;
        try {
            while (cursor.hasNext()) {
                i++;

                int db = 0;
                List<String> row = new ArrayList();
                DBObject o = cursor.next();
                String key = (String) o.get("key");
                if (key != null) {
                    for (Attribute attr : tempTable.getPrimaryKeys()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(key, 0, nr);
                        row.add(v);
                        key = key.substring(nr);
                        if (attr.getName().equals(theOne.getName())) {
                            place = db;
                        }
                        db++;
                    }
                }
                String value = (String) o.get("value");
                if (value != null) {
                    for (Attribute attr : tempTable.getAttributes()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(value, 0, nr);
                        row.add(v);
                        value = value.substring(nr);
                        if (attr.getName().equals(theOne.getName())) {
                            place = db;
                        }
                        db++;
                    }
                    for (Attribute attr : tempTable.getForeignKeys()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(value, 0, nr);
                        row.add(v);
                        value = value.substring(nr);
                        if (attr.getName().equals(theOne.getName())) {
                            place = db;
                        }
                        db++;
                    }
                }

                int k = 0;
                int poz = 0;
                for (List<String> r : values) {
                    if (!comp(r.get(place), row.get(place), orderType)) {
                        poz = k;
                        break;
                    }
                    k++;
                }
                if (poz == 0) {
                    poz = k;
                }
                values.add(poz, row);
                if (values.size() == maxnr) {
                    pack++;
                    for (List<String> r : values) {
                        MongoDB.saveCollection(table1, r);
                    }
                    values = new ArrayList();
                }
            }
        } finally {
            cursor.close();
        }
        if (values.size() > 0) {
            pack++;
            for (List<String> r : values) {
                MongoDB.saveCollection(table1, r);
            }
        }
        int[] packArray = new int[pack];
        List<Integer> readIn = new ArrayList();
        for (Integer l = 0; l < i; l += 5) {
            readIn.add(l);
        }
        coll = MongoDB.db.getCollection(table1.getName());
        cursor = coll.find();
        List<List<String>> minValues = MongoDB.getSpecElements(table1, cursor, readIn);
        while (sortedValues.size() != i) {
            List<String> tmin = min(minValues, place, orderType);
            int ind = 0;
            int next = 0;
            for (List<String> mins : minValues) {
                if (mins.equals(tmin)) {
                    packArray[ind]++;
                    next = ind * 5 + packArray[ind];
                    if (readIn.contains(next) || next >= i) {
                        next = nextfit(readIn, i);
                    }
                    readIn.add(next);
                    break;
                }
                ind++;
            }
            sortedValues.add(tmin);
            List<Integer> newVal = new ArrayList();
            if (next == 0) {
                minValues.remove(ind);
            } else {
                newVal.add(next);
                cursor = coll.find();
                minValues.set(ind, MongoDB.getSpecElements(table1, cursor, newVal).get(0));
            }
        }
        coll.drop();
        return sortedValues;
    }

    public static List<List<String>> oderBy(List<List<String>> theValues, Table tempTable, Attribute theOne, String orderType) {
        //oderType: ASC or DESC
        List<List<String>> values = new ArrayList();
        List<List<String>> sortedValues = new ArrayList();
        int i = 0;
        int pack = 0;
        int maxnr = 5;
        Table table1 = new Table(tempTable.getName() + "partsort");
        table1.setPrimaryKeys(tempTable.getPrimaryKeys());
        table1.setAttributes(tempTable.getAttributes());
        table1.setForeignKeys(tempTable.getForeignKeys());
        int db = 0;
        int place = 0;
        for (Attribute attr : tempTable.getPrimaryKeys()) {
            if (attr.getName().equals(theOne.getName())) {
                place = db;
            }
            db++;
        }
        for (Attribute attr : tempTable.getAttributes()) {
            if (attr.getName().equals(theOne.getName())) {
                place = db;
            }
            db++;
        }
        for (Attribute attr : tempTable.getForeignKeys()) {
            if (attr.getName().equals(theOne.getName())) {
                place = db;
            }
            db++;
        }

        for (int ii = 0; ii < theValues.size(); ii++) {
            i++;

            List<String> row = theValues.get(ii);

            int k = 0;
            int poz = 0;
            for (List<String> r : values) {
                if (!comp(r.get(place), row.get(place), orderType)) {
                    poz = k;
                    break;
                }
                k++;
            }
            if (poz == 0) {
                poz = k;
            }
            values.add(poz, row);
            if (values.size() == maxnr) {
                pack++;
                for (List<String> r : values) {
                    MongoDB.saveCollection(table1, r);
                }
                values = new ArrayList();
            }
        }
        if (values.size() > 0) {
            pack++;
            for (List<String> r : values) {
                MongoDB.saveCollection(table1, r);
            }
        }
        int[] packArray = new int[pack];
        List<Integer> readIn = new ArrayList();
        for (Integer l = 0; l < i; l += 5) {
            readIn.add(l);
        }
        DBCollection coll = MongoDB.db.getCollection(table1.getName());
        DBCursor cursor = coll.find();
        List<List<String>> minValues = MongoDB.getSpecElements(table1, cursor, readIn);
        while (sortedValues.size() != i) {
            List<String> tmin = min(minValues, place, orderType);
            int ind = 0;
            int next = 0;
            for (List<String> mins : minValues) {
                if (mins.equals(tmin)) {
                    packArray[ind]++;
                    next = ind * 5 + packArray[ind];
                    if (readIn.contains(next) || next >= i) {
                        next = nextfit(readIn, i);
                    }
                    readIn.add(next);
                    break;
                }
                ind++;
            }
            sortedValues.add(tmin);
            List<Integer> newVal = new ArrayList();
            if (next == 0) {
                minValues.remove(ind);
            } else {
                newVal.add(next);
                cursor = coll.find();
                minValues.set(ind, MongoDB.getSpecElements(table1, cursor, newVal).get(0));
            }
        }
        coll.drop();
        return sortedValues;
    }

    private static boolean comp(String first, String second, String sort) {
        if (sort.equals("ASC")) {
            if (first.compareTo(second) < 0) {
                return true;
            } else {
                return false;
            }
        } else {
            if (first.compareTo(second) > 0) {
                return true;
            } else {
                return false;
            }
        }
    }

    private static List<String> min(List<List<String>> values, int place, String orderType) {
        List<String> minim = values.get(0);
        for (int i = 0; i < values.size() - 1; i++) {
            if (!comp(values.get(i).get(place), values.get(i + 1).get(place), orderType)) {
                minim = values.get(i + 1);
            }
        }
        return minim;
    }

    private static Integer nextfit(List<Integer> readIn, int size) {
        Integer next = 0;
        for (Integer i = 0; i < size; i++) {
            if (!readIn.contains(i)) {
                return i;
            }
        }
        return next;
    }

    public static List<List<String>> groupBy(Table tempTable, Attribute groupOne, Attribute theOne, String function, String havingAttrName, String havingOp, String havingValue) {
        DBCollection coll = MongoDB.db.getCollection(tempTable.getName());
        DBCursor cursor = coll.find();
        List<List<String>> values = new ArrayList();
        List<List<String>> groupedValues = new ArrayList();
        int i = 0;
        int maxnr = 5;
        Table table1 = SerializationUtils.clone(tempTable);
        table1.setName(table1.getName() + "partgroup");
        List<Attribute> newAttrList = new ArrayList();
        for (Attribute attr : tempTable.getPrimaryKeys()) {
            if (attr.getName().equals(theOne.getName()) || attr.getName().equals(groupOne.getName())) {
                newAttrList.add(attr);
            }
        }
        for (Attribute attr : tempTable.getAttributes()) {
            if (attr.getName().equals(theOne.getName()) || attr.getName().equals(groupOne.getName())) {
                newAttrList.add(attr);
            }
        }
        for (Attribute attr : tempTable.getForeignKeys()) {
            if (attr.getName().equals(theOne.getName()) || attr.getName().equals(groupOne.getName())) {
                newAttrList.add(attr);
            }
        }
        table1.setAttributes(newAttrList);
        if (function.equals("SUM")) {
            table1.addAttribute(new Attribute("SUM", "int", 4, false));
        } else if (function.equals("COUNT")) {
            table1.addAttribute(new Attribute("COUNT", "int", 4, false));
        } else if (function.equals("AVG")) {
            table1.addAttribute(new Attribute("SUM", "int", 4, false));
            table1.addAttribute(new Attribute("COUNT", "int", 4, false));
        }
        int groupPlace = 0;
        int onePlace = 0;
        int db = 0;
        try {
            while (cursor.hasNext()) {
                db = 0;
                List<String> row = new ArrayList();
                DBObject o = cursor.next();
                String key = (String) o.get("key");
                if (key != null) {
                    for (Attribute attr : tempTable.getPrimaryKeys()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(key, 0, nr);
                        if (attr.getName().equals(theOne.getName()) || attr.getName().equals(groupOne.getName())) {
                            row.add(v);
                            db++;
                        }
                        key = key.substring(nr);
                        if (attr.getName().equals(theOne.getName())) {
                            onePlace = db - 1;
                        }
                        if (attr.getName().equals(groupOne.getName())) {
                            groupPlace = db - 1;
                        }
                    }
                }
                String value = (String) o.get("value");
                if (value != null) {
                    for (Attribute attr : tempTable.getAttributes()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(value, 0, nr);
                        if (attr.getName().equals(theOne.getName()) || attr.getName().equals(groupOne.getName())) {
                            row.add(v);
                            db++;
                        }
                        value = value.substring(nr);
                        if (attr.getName().equals(theOne.getName())) {
                            onePlace = db - 1;
                        }
                        if (attr.getName().equals(groupOne.getName())) {
                            groupPlace = db - 1;
                        }

                    }
                    for (Attribute attr : tempTable.getForeignKeys()) {
                        int nr = attr.getLength();
                        String v = StringUtils.substring(value, 0, nr);
                        if (attr.getName().equals(theOne.getName()) || attr.getName().equals(groupOne.getName())) {
                            row.add(v);
                            db++;
                        }
                        value = value.substring(nr);
                        if (attr.getName().equals(theOne.getName())) {
                            onePlace = db - 1;
                        }
                        if (attr.getName().equals(groupOne.getName())) {
                            groupPlace = db - 1;
                        }
                    }
                    if (function.equals("SUM")) {
                        row.add(row.get(onePlace));
                    } else if (function.equals("COUNT")) {
                        row.add("1");
                    } else if (function.equals("AVG")) {
                        row.add(row.get(onePlace));
                        row.add("1");
                    }
                }

                int k = 0;
                int poz = 0;
                boolean got = false;
                for (List<String> r : values) {
                    if (r.get(groupPlace).equals(row.get(groupPlace))) {
                        poz = k;
                        got = true;
                        break;
                    }
                    k++;
                }
                if (values.size() > 0) {
                    if (!got) {
                        values.add(k, row);
                        i++;
                    } else {
                        List<String> r = values.get(poz);
                        if (function.equals("SUM")) {
                            int sumNr = Integer.parseInt(r.get(db).trim()) + Integer.parseInt(row.get(db).trim());
                            r.set(db, Integer.toString(sumNr));
                        } else if (function.equals("COUNT")) {
                            r.set(db, Integer.toString(Integer.parseInt(r.get(db).trim()) + 1));
                        } else if (function.equals("AVG")) {
                            int sumNr = Integer.parseInt(r.get(db).trim()) + Integer.parseInt(row.get(db).trim());
                            r.set(db, Integer.toString(sumNr));
                            r.set(db + 1, Integer.toString(Integer.parseInt(r.get(db + 1).trim()) + 1));
                        }
                    }
                } else {
                    values.add(poz, row);
                    i++;
                }
                if (values.size() == maxnr) {
                    for (List<String> r : values) {
                        MongoDB.saveSpecCollection(table1, r);
                    }
                    values = new ArrayList();
                }
            }
        } finally {
            cursor.close();
        }
        i--;
        if (values.size() > 0) {
            for (List<String> r : values) {
                MongoDB.saveSpecCollection(table1, r);
            }
        }
        List<Integer> readIn = new ArrayList();
        Integer p;
        if (i > 5) {
            for (Integer l = 0; l < 5; l++) {
                readIn.add(l);
            }
            p = 4;
        } else {
            readIn.add(0);
            p = 0;
        }
        coll = MongoDB.db.getCollection(table1.getName());
        cursor = coll.find();
        groupedValues = MongoDB.getSpecElements(table1, cursor, readIn);
        while (p != i - 1) {
            p++;
            List<Integer> newVal = new ArrayList();
            newVal.add(p);
            cursor = coll.find();
            List<String> row = MongoDB.getSpecElements(table1, cursor, newVal).get(0);
            int k = 0;
            int poz = 0;
            boolean got = false;
            for (List<String> r : groupedValues) {
                if (r.get(groupPlace).equals(row.get(groupPlace))) {
                    poz = k;
                    got = true;
                    break;
                }
                k++;
            }
            if (!got) {
                groupedValues.add(k, row);
                i++;
            } else {
                List<String> r = groupedValues.get(poz);
                if (function.equals("SUM")) {
                    int sumNr = Integer.parseInt(r.get(db).trim()) + Integer.parseInt(row.get(db).trim());
                    r.set(db, Integer.toString(sumNr));
                } else if (function.equals("COUNT")) {
                    r.set(db, Integer.toString(Integer.parseInt(r.get(db).trim()) + 1));
                } else if (function.equals("AVG")) {
                    int sumNr = Integer.parseInt(r.get(db).trim()) + Integer.parseInt(row.get(db).trim());
                    r.set(db, Integer.toString(sumNr));
                    r.set(db + 1, Integer.toString(Integer.parseInt(r.get(db + 1).trim()) + 1));
                }
            }

        }

        coll.drop();
        int del = 0;
        for (Attribute attr : table1.getAttributes()) {
            if (attr.getName().equals(theOne.getName())) {
                break;
            }
            del++;
        }

        if (havingAttrName != null) {
            int thePoz;
            if (havingAttrName.equals(groupOne.getName())) {
                thePoz = groupPlace;
            } else {
                thePoz = db;
            }
            List<List<String>> goodValues = new ArrayList();
            for (List<String> r : groupedValues) {
                if (function.equals("AVG")) {
                    double d = Double.parseDouble(r.get(thePoz).trim()) / Double.parseDouble(r.get(thePoz + 1).trim());
                    if (checkMatch(String.valueOf(d), havingOp, havingValue)) {
                        r.set(thePoz, String.valueOf(d));
                        r.remove(thePoz + 1);
                        goodValues.add(r);
                    }
                } else {
                    if (checkMatch(r.get(thePoz), havingOp, havingValue)) {
                        goodValues.add(r);
                    }
                }
            }
            for (int c = 0; c < goodValues.size(); c++) {
                goodValues.get(c).remove(del);
            }
            return goodValues;
        } else {
            if (function.equals("AVG")) {
                for (List<String> r : groupedValues) {
                    double d = Double.parseDouble(r.get(db).trim()) / Double.parseDouble(r.get(db + 1).trim());
                    r.set(db, String.valueOf(d));
                    r.remove(db + 1);

                }
            }
        }
        for (int c = 0; c < groupedValues.size(); c++) {
            groupedValues.get(c).remove(del);
        }
        return groupedValues;
    }

    public static List<List<String>> groupBy(List<List<String>> theValues, Table tempTable, Attribute groupOne, Attribute theOne, String function, String havingAttrName, String havingOp, String havingValue) {
        List<List<String>> values = new ArrayList();
        List<List<String>> groupedValues = new ArrayList();
        int i = 0;
        int maxnr = 5;
        Table table1 = SerializationUtils.clone(tempTable);
        table1.setName(table1.getName() + "partgroup");
        List<Attribute> newAttrList = new ArrayList();
        for (Attribute attr : tempTable.getPrimaryKeys()) {
            if (attr.getName().equals(theOne.getName()) || attr.getName().equals(groupOne.getName())) {
                newAttrList.add(attr);
            }
        }
        for (Attribute attr : tempTable.getAttributes()) {
            if (attr.getName().equals(theOne.getName()) || attr.getName().equals(groupOne.getName())) {
                newAttrList.add(attr);
            }
        }
        for (Attribute attr : tempTable.getForeignKeys()) {
            if (attr.getName().equals(theOne.getName()) || attr.getName().equals(groupOne.getName())) {
                newAttrList.add(attr);
            }
        }
        table1.setAttributes(newAttrList);
        if (function.equals("SUM")) {
            table1.addAttribute(new Attribute("SUM", "int", 4, false));
        } else if (function.equals("COUNT")) {
            table1.addAttribute(new Attribute("COUNT", "int", 4, false));
        } else if (function.equals("AVG")) {
            table1.addAttribute(new Attribute("SUM", "int", 4, false));
            table1.addAttribute(new Attribute("COUNT", "int", 4, false));
        }
        int groupPlace = 0;
        int onePlace = 0;
        int db = 0;
        for (int ii = 0; ii < theValues.size(); ii++) {
            db = 0;
            List<String> row = theValues.get(ii);
            for (Attribute attr : tempTable.getPrimaryKeys()) {
                if (!attr.getName().equals(theOne.getName()) && !attr.getName().equals(groupOne.getName())) {
                    row.remove(db);
                } else {
                    db++;
                }
                if (attr.getName().equals(theOne.getName())) {
                    onePlace = db - 1;
                }
                if (attr.getName().equals(groupOne.getName())) {
                    groupPlace = db - 1;
                }
            }

            for (Attribute attr : tempTable.getAttributes()) {
                if (!attr.getName().equals(theOne.getName()) && !attr.getName().equals(groupOne.getName())) {
                    row.remove(db);
                } else {
                    db++;
                }
                if (attr.getName().equals(theOne.getName())) {
                    onePlace = db - 1;
                }
                if (attr.getName().equals(groupOne.getName())) {
                    groupPlace = db - 1;
                }

            }
            for (Attribute attr : tempTable.getForeignKeys()) {
                if (!attr.getName().equals(theOne.getName()) && !attr.getName().equals(groupOne.getName())) {
                    row.remove(db);
                } else {
                    db++;
                }
                if (attr.getName().equals(theOne.getName())) {
                    onePlace = db - 1;
                }
                if (attr.getName().equals(groupOne.getName())) {
                    groupPlace = db - 1;
                }
            }
            if (function.equals("SUM")) {
                row.add(row.get(onePlace));
            } else if (function.equals("COUNT")) {
                row.add("1");
            } else if (function.equals("AVG")) {
                row.add(row.get(onePlace));
                row.add("1");
            }

            int k = 0;
            int poz = 0;
            boolean got = false;
            for (List<String> r : values) {
                if (r.get(groupPlace).equals(row.get(groupPlace))) {
                    poz = k;
                    got = true;
                    break;
                }
                k++;
            }
            if (values.size() > 0) {
                if (!got) {
                    values.add(k, row);
                    i++;
                } else {
                    List<String> r = values.get(poz);
                    if (function.equals("SUM")) {
                        int sumNr = Integer.parseInt(r.get(db).trim()) + Integer.parseInt(row.get(db).trim());
                        r.set(db, Integer.toString(sumNr));
                    } else if (function.equals("COUNT")) {
                        r.set(db, Integer.toString(Integer.parseInt(r.get(db).trim()) + 1));
                    } else if (function.equals("AVG")) {
                        int sumNr = Integer.parseInt(r.get(db).trim()) + Integer.parseInt(row.get(db).trim());
                        r.set(db, Integer.toString(sumNr));
                        r.set(db + 1, Integer.toString(Integer.parseInt(r.get(db + 1).trim()) + 1));
                    }
                }
            } else {
                values.add(poz, row);
                i++;
            }
            if (values.size() == maxnr) {
                for (List<String> r : values) {
                    MongoDB.saveSpecCollection(table1, r);
                }
                values = new ArrayList();
            }
        }

        i--;
        if (values.size()
                > 0) {
            for (List<String> r : values) {
                MongoDB.saveSpecCollection(table1, r);
            }
        }
        List<Integer> readIn = new ArrayList();
        Integer p;
        if (i
                > 5) {
            for (Integer l = 0; l < 5; l++) {
                readIn.add(l);
            }
            p = 4;
        } else {
            readIn.add(0);
            p = 0;
        }
        DBCollection coll = MongoDB.db.getCollection(table1.getName());
        DBCursor cursor = coll.find();
        groupedValues = MongoDB.getSpecElements(table1, cursor, readIn);
        while (p != i - 1) {
            p++;
            List<Integer> newVal = new ArrayList();
            newVal.add(p);
            cursor = coll.find();
            List<String> row = MongoDB.getSpecElements(table1, cursor, newVal).get(0);
            int k = 0;
            int poz = 0;
            boolean got = false;
            for (List<String> r : groupedValues) {
                if (r.get(groupPlace).equals(row.get(groupPlace))) {
                    poz = k;
                    got = true;
                    break;
                }
                k++;
            }
            if (!got) {
                groupedValues.add(k, row);
                i++;
            } else {
                List<String> r = groupedValues.get(poz);
                if (function.equals("SUM")) {
                    int sumNr = Integer.parseInt(r.get(db).trim()) + Integer.parseInt(row.get(db).trim());
                    r.set(db, Integer.toString(sumNr));
                } else if (function.equals("COUNT")) {
                    r.set(db, Integer.toString(Integer.parseInt(r.get(db).trim()) + 1));
                } else if (function.equals("AVG")) {
                    int sumNr = Integer.parseInt(r.get(db).trim()) + Integer.parseInt(row.get(db).trim());
                    r.set(db, Integer.toString(sumNr));
                    r.set(db + 1, Integer.toString(Integer.parseInt(r.get(db + 1).trim()) + 1));
                }
            }

        }

        coll.drop();
        int del = 0;
        for (Attribute attr
                : table1.getAttributes()) {
            if (attr.getName().equals(theOne.getName())) {
                break;
            }
            del++;
        }

        if (havingAttrName
                != null) {
            int thePoz;
            if (havingAttrName.equals(groupOne.getName())) {
                thePoz = groupPlace;
            } else {
                thePoz = db;
            }
            List<List<String>> goodValues = new ArrayList();
            for (List<String> r : groupedValues) {
                if (function.equals("AVG")) {
                    double d = Double.parseDouble(r.get(thePoz).trim()) / Double.parseDouble(r.get(thePoz + 1).trim());
                    if (checkMatch(String.valueOf(d), havingOp, havingValue)) {
                        goodValues.add(r);
                    }
                } else {
                    if (checkMatch(r.get(thePoz), havingOp, havingValue)) {
                        goodValues.add(r);
                    }
                }
            }
            for (int c = 0; c < goodValues.size(); c++) {
                goodValues.get(c).remove(del);
            }
            return goodValues;
        } else {
            if (function.equals("AVG")) {
                for (List<String> r : groupedValues) {
                    double d = Double.parseDouble(r.get(db).trim()) / Double.parseDouble(r.get(db + 1).trim());
                    r.set(db, String.valueOf(d));
                    r.remove(db + 1);

                }
            }
        }
        for (int c = 0;
                c < groupedValues.size();
                c++) {
            groupedValues.get(c).remove(del);
        }
        return groupedValues;
    }
}
